package propertymgmt.entities;

import java.io.Serializable;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.Date;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToOne;
import javax.persistence.Version;

import propertymgmt.constants.Constants.PaymentType;
import propertymgmt.interfaces.Reportable;

/**
 * Tenant.java
 * 
 * Represent a Tenant.  This class is an entity, which means
 * it will be persisted in the database.
 * 
 * @author Brian
 *
 */
@SuppressWarnings("serial")
@Entity(name = "TENANT")
@NamedQueries({
	@NamedQuery(name = "Tenant.getTenantById",
				query = "SELECT t FROM TENANT t WHERE t.tenantId = :tenantId")
})

public class Tenant implements Serializable, Reportable{
	
	@Id
	@Column(name = "TENANT_ID", nullable = false)
	@GeneratedValue(strategy = GenerationType.AUTO)
	private long tenantId;

	@Column(name = "FIRST_NAME", nullable = false)
	private String firstName = null;
	
	@Column(name = "LAST_NAME", nullable = false)
	private String lastName = null;
	
	@Column(name = "AGE")
	private int tenantAge = 0;
	
	@Version
	@Column(name = "LAST_UPDATED_TIME")
	private Date updatedTime;
	
	@OneToOne(fetch=FetchType.LAZY)
	private Lease lease = null;
	
	/**
	 * Default Constructor
	 */
	public Tenant(){
		
	}
	
	/**
	 * Constructor
	 * 
	 * @param firstName Tenant first name
	 * @param lastName Tenant last name
	 * @param tenantAge Tenant age
	 */
	public Tenant(String firstName, String lastName, int tenantAge) {
		this.firstName = firstName;
		this.lastName = lastName;
		this.tenantAge = tenantAge;
	}
	
	/**
	 * Sets the Lease associated with this Tenant
	 * @param lease
	 */
	public void setLease(Lease lease){
		this.lease = lease;
	}
	
	/**
	 * Returns the Lease associated with this Tenant
	 * @return Lease
	 */
	public Lease getLease(){
		return lease;
	}

	/**
	 * @return the firstName
	 */
	public String getFirstName() {
		return firstName;
	}

	/**
	 * @param firstName the firstName to set
	 */
	public void setFirstName(String firstName) {
		this.firstName = firstName;
	}
	
	/**
	 * @return the lastName
	 */
	public String getLastName() {
		return lastName;
	}

	/**
	 * @param lastName the lastName to set
	 */
	public void setLastName(String lastName) {
		this.lastName = lastName;
	}

	/**
	 * @return the tenantAge
	 */
	public int getTenantAge() {
		return tenantAge;
	}

	/**
	 * @param tenantAge the tenantAge to set
	 */
	public void setTenantAge(int tenantAge) {
		this.tenantAge = tenantAge;
	}
	
	/**
	 * @return id
	 */
	public long getId() {
		return tenantId;
	}
	
	/* (non-Javadoc)
	 * @see propertymgmt.interfaces.Reportable#generateReport()
	 */
	public String generateReport(){
		SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yyyy");
		DecimalFormat df = new DecimalFormat("0.00");
		
		StringBuffer strBuffer = new StringBuffer("For tenant " + getFirstName() + " " + getLastName() + ":");
		
		double amountReceivedFromTenant = 0.0;
		double amountPaidToTenant = 0.0;
		
		for(Payment payment : lease.getPayments()){
			for(Tenant t2 : payment.getAssociatedTenants()){
				if(this.equals(t2)){
					PaymentType paymentType = payment.getType();
					
					if(paymentType.equals(PaymentType.FINE)){
						strBuffer.append("\n\tAssociated with FINE for $" + df.format(payment.getAmount()) +
							" on " + sdf.format(payment.getDateReceived()));
					}else if(paymentType.isIncoming()){
						String str = "\n\tPaid $" + df.format(payment.getAmount());
						
						if(paymentType.equals(PaymentType.RENT)){
							double paymentDelta = lease.getRentAmount() - payment.getAmount();
							boolean partialPayment = (paymentDelta > 0.);
							
							if(partialPayment){
								str += " (Partial payment, still owes $" + df.format(paymentDelta) + ")";
							}
						}
						
						str += " on " + sdf.format(payment.getDateReceived()) + " (" + payment.getPaymentName() + ")";
						amountReceivedFromTenant += payment.getAmount();
						
						if(paymentType.equals(PaymentType.RENT)){
							int numDaysLate = payment.getNumDaysLate(payment.getRentPaymentMonth(),
									payment.getRentPaymentYear(), lease.getRentLateDate());
							if(numDaysLate != 0){
								double lateFeeOwed = lease.getLateRentFee() + (lease.getLateRentFeePerDay() * numDaysLate);
								lateFeeOwed -= (payment.getAmount() - lease.getRentAmount());
								str += " ** Late, owes $" + df.format(lateFeeOwed) + " **";
							}
						}
						
						strBuffer.append(str);
					}else if(paymentType.equals(PaymentType.REFUND)){
						strBuffer.append("\n\tRefunded $" + df.format(payment.getAmount()) + " on " +
								sdf.format(payment.getDateReceived()));
						amountPaidToTenant += payment.getAmount();
					}
					
					break;
				}
			}
		}
		
		if(amountReceivedFromTenant != 0.0){
			strBuffer.append("\n\n\tTotal received from " + getFirstName() + " " + getLastName() + ": $" + amountReceivedFromTenant);
			strBuffer.append("\n\tTotal paid to " + getFirstName() + " " + getLastName() + ": $" + amountPaidToTenant);
		}
		
		return strBuffer.toString();
	}
	
	/* (non-Javadoc)
	 * @see java.lang.Object#hashCode()
	 */
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result
				+ ((firstName == null) ? 0 : firstName.hashCode());
		result = prime * result
				+ ((lastName == null) ? 0 : lastName.hashCode());
		result = prime * result + tenantAge;
		return result;
	}

	/* (non-Javadoc)
	 * @see java.lang.Object#equals(java.lang.Object)
	 */
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Tenant other = (Tenant) obj;
		if (firstName == null) {
			if (other.firstName != null)
				return false;
		} else if (!firstName.equals(other.firstName))
			return false;
		if (lastName == null) {
			if (other.lastName != null)
				return false;
		} else if (!lastName.equals(other.lastName))
			return false;
		if (tenantAge != other.tenantAge)
			return false;
		return true;
	}

	/* (non-Javadoc)
	 * @see java.lang.Object#toString()
	 */
	public String toString(){
		return lastName + ", " + firstName;
	}
}